﻿using System;
using System.IO;
using System.Text;

public class ROM
{
    internal bool batteryRam;
    public const int CHRROM_MIRRORING = 7;
    protected internal long crc32;
    internal bool enableSave = true;
    internal bool failedSaveFile;
    internal string fileName;
    internal bool fourScreen;
    public const int FOURSCREEN_MIRRORING = 2;
    internal short[] header;
    public const int HORIZONTAL_MIRRORING = 1;
    internal static string[] mapperName = new string[0xff];
    internal static bool[] mapperSupported_Renamed_Field = new bool[0xff];
    internal int mapperType;
    internal int mirroring;
    internal NES nes;
    internal FileStream raFile;
    internal short[][] rom;
    internal int romCount;
    internal short[] saveRam;
    internal bool saveRamUpToDate = true;
    public const int SINGLESCREEN_MIRRORING = 3;
    public const int SINGLESCREEN_MIRRORING2 = 4;
    public const int SINGLESCREEN_MIRRORING3 = 5;
    public const int SINGLESCREEN_MIRRORING4 = 6;
    internal bool trainer;
    internal bool valid;
    public const int VERTICAL_MIRRORING = 0;
    internal short[][] vrom;
    internal int vromCount;
    internal Tile[][] vromTile;

    static ROM()
    {
        for (int i = 0; i < 0xff; i++)
        {
            mapperName[i] = "Unknown Mapper";
        }
        mapperName[0] = "NROM";
        mapperName[1] = "Nintendo MMC1";
        mapperName[2] = "UxROM";
        mapperName[3] = "CNROM";
        mapperName[4] = "Nintendo MMC3";
        mapperName[5] = "Nintendo MMC5";
        mapperName[6] = "FFE F4xxx";
        mapperName[7] = "AxROM";
        mapperName[8] = "FFE F3xxx";
        mapperName[9] = "Nintendo MMC2";
        mapperName[10] = "Nintendo MMC4";
        mapperName[11] = "Color Dreams";
        mapperName[12] = "FFE F6xxx";
        mapperName[13] = "CPROM";
        mapperName[15] = "iNES Mapper #015";
        mapperName[0x10] = "Bandai";
        mapperName[0x11] = "FFE F8xxx";
        mapperName[0x12] = "Jaleco SS8806";
        mapperName[0x13] = "Namcot 106";
        mapperName[20] = "(Hardware) Famicom Disk System";
        mapperName[0x15] = "Konami VRC4a, VRC4c";
        mapperName[0x16] = "Konami VRC2a";
        mapperName[0x17] = "Konami VRC2b, VRC4e, VRC4f";
        mapperName[0x18] = "Konami VRC6a";
        mapperName[0x19] = "Konami VRC4b, VRC4d";
        mapperName[0x1a] = "Konami VRC6b";
        mapperName[0x20] = "Irem G-101";
        mapperName[0x21] = "Taito TC0190, TC0350";
        mapperName[0x22] = "BxROM, NINA-001";
        mapperName[0x29] = "Caltron 6-in-1";
        mapperName[0x2e] = "Rumblestation 15-in-1";
        mapperName[0x2f] = "Nintendo MMC3 Multicart (Super Spike V'Ball + Nintendo World Cup)";
        mapperName[0x30] = "iNES Mapper #048";
        mapperName[0x40] = "Tengen RAMBO-1";
        mapperName[0x41] = "Irem H-3001";
        mapperName[0x42] = "GxROM";
        mapperName[0x43] = "Sunsoft 3";
        mapperName[0x44] = "Sunsoft 4";
        mapperName[0x45] = "Sunsoft FME-7";
        mapperName[70] = "iNES Mapper #070";
        mapperName[0x47] = "Camerica";
        mapperName[0x48] = "iNES Mapper #072";
        mapperName[0x49] = "Konami VRC3";
        mapperName[0x4b] = "Konami VRC1";
        mapperName[0x4c] = "iNES Mapper #076 (Digital Devil Monogatari - Megami Tensei)";
        mapperName[0x4d] = "iNES Mapper #077 (Napoleon Senki)";
        mapperName[0x4e] = "Irem 74HC161/32";
        mapperName[0x4f] = "American Game Cartridges";
        mapperName[80] = "iNES Mapper #080";
        mapperName[0x52] = "iNES Mapper #082";
        mapperName[0x55] = "Konami VRC7a, VRC7b";
        mapperName[0x56] = "iNES Mapper #086 (Moero!! Pro Yakyuu)";
        mapperName[0x57] = "iNES Mapper #087";
        mapperName[0x58] = "iNES Mapper #088";
        mapperName[0x59] = "iNES Mapper #087 (Mito Koumon)";
        mapperName[0x5c] = "iNES Mapper #092";
        mapperName[0x5d] = "iNES Mapper #093 (Fantasy Zone)";
        mapperName[0x5e] = "iNES Mapper #094 (Senjou no Ookami)";
        mapperName[0x5f] = "iNES Mapper #095 (Dragon Buster) [MMC3 Derived]";
        mapperName[0x60] = "(Hardware) Oeka Kids Tablet";
        mapperName[0x61] = "iNES Mapper #097 (Kaiketsu Yanchamaru)";
        mapperName[0x69] = "NES-EVENT [MMC1 Derived]";
        mapperName[0x71] = "iNES Mapper #113";
        mapperName[0x73] = "iNES Mapper #115 (Yuu Yuu Hakusho Final) [MMC3 Derived]";
        mapperName[0x76] = "iNES Mapper #118 [MMC3 Derived]";
        mapperName[0x77] = "TQROM";
        mapperName[140] = "iNES Mapper #140 (Bio Senshi Dan)";
        mapperName[0x98] = "iNES Mapper #152";
        mapperName[0x9a] = "iNES Mapper #152 (Devil Man)";
        mapperName[0x9f] = "Bandai (Alternate of #016)";
        mapperName[180] = "(Hardware) Crazy Climber Controller";
        mapperName[0xb6] = "iNES Mapper #182";
        mapperName[0xb8] = "iNES Mapper #184";
        mapperName[0xb9] = "iNES Mapper #185";
        mapperName[0xcf] = "iNES Mapper #185 (Fudou Myouou Den)";
        mapperName[0xe4] = "Active Enterprises";
        mapperName[0xe8] = "Camerica (Quattro series)";
        mapperSupported_Renamed_Field[0] = true;
        mapperSupported_Renamed_Field[1] = true;
        mapperSupported_Renamed_Field[2] = true;
        mapperSupported_Renamed_Field[3] = true;
        mapperSupported_Renamed_Field[4] = true;
        mapperSupported_Renamed_Field[7] = true;
        mapperSupported_Renamed_Field[9] = true;
        mapperSupported_Renamed_Field[10] = true;
        mapperSupported_Renamed_Field[11] = true;
        mapperSupported_Renamed_Field[15] = true;
        mapperSupported_Renamed_Field[0x12] = true;
        mapperSupported_Renamed_Field[0x15] = true;
        mapperSupported_Renamed_Field[0x16] = true;
        mapperSupported_Renamed_Field[0x17] = true;
        mapperSupported_Renamed_Field[0x20] = true;
        mapperSupported_Renamed_Field[0x21] = true;
        mapperSupported_Renamed_Field[0x22] = true;
        mapperSupported_Renamed_Field[0x30] = true;
        mapperSupported_Renamed_Field[0x40] = true;
        mapperSupported_Renamed_Field[0x42] = true;
        mapperSupported_Renamed_Field[0x44] = true;
        mapperSupported_Renamed_Field[0x47] = true;
        mapperSupported_Renamed_Field[0x48] = true;
        mapperSupported_Renamed_Field[0x4b] = true;
        mapperSupported_Renamed_Field[0x4e] = true;
        mapperSupported_Renamed_Field[0x4f] = true;
        mapperSupported_Renamed_Field[0x57] = true;
        mapperSupported_Renamed_Field[0x5e] = true;
        mapperSupported_Renamed_Field[0x69] = true;
        mapperSupported_Renamed_Field[140] = true;
        mapperSupported_Renamed_Field[0xb6] = true;
        mapperSupported_Renamed_Field[0xe8] = true;
    }

    public ROM(NES nes)
    {
        this.nes = nes;
        this.valid = false;
    }

    public virtual void closeRom()
    {
        if (this.batteryRam && !this.saveRamUpToDate)
        {
            try
            {
                StringBuilder builder = new StringBuilder((this.saveRam.Length * 2) + (this.saveRam.Length / 0x26));
                for (int i = 0; i < this.saveRam.Length; i++)
                {
                    string str = string.Format("%02x", this.saveRam[i] & 0xff);
                    if (((i % 0x26) == 0) && (i != 0))
                    {
                        builder.Append(" ");
                    }
                    builder.Append(str);
                }
                builder.ToString();
                this.saveRamUpToDate = true;
            }
            catch (Exception)
            {
            }
        }
    }

    public virtual MemoryMapper createMapper()
    {
        if (this.mapperSupported())
        {
            switch (this.mapperType)
            {
                case 0:
                    return new MapperDefault();

                case 1:
                    return new Mapper001();

                case 2:
                    return new Mapper002();

                case 3:
                    return new Mapper003();

                case 4:
                    return new Mapper004();

                case 7:
                    return new Mapper007();

                case 9:
                    return new Mapper009();

                case 10:
                    return new Mapper010();

                case 11:
                    return new Mapper011();

                case 15:
                    return new Mapper015();

                case 0x12:
                    return new Mapper018();

                case 0x15:
                    return new Mapper021();

                case 0x16:
                    return new Mapper022();

                case 0x17:
                    return new Mapper023();

                case 0x20:
                    return new Mapper032();

                case 0x21:
                    return new Mapper033();

                case 0x22:
                    return new Mapper034();

                case 0x40:
                    return new Mapper064();

                case 0x42:
                    return new Mapper066();

                case 0x44:
                    return new Mapper068();

                case 0x47:
                    return new Mapper071();

                case 0x48:
                    return new Mapper072();

                case 0x30:
                    return new Mapper048();

                case 0x4b:
                    return new Mapper075();

                case 0x4e:
                    return new Mapper078();

                case 0x4f:
                    return new Mapper079();

                case 0x57:
                    return new Mapper087();

                case 0x5e:
                    return new Mapper094();

                case 0x69:
                    return new Mapper105();

                case 140:
                    return new Mapper140();

                case 0xb6:
                    return new Mapper182();

                case 0xe8:
                    return new Mapper232();
            }
        }
        return new MapperDefault();
    }

    public virtual void destroy()
    {
        this.closeRom();
        this.nes = null;
    }

    public virtual short[] getRomBank(int bank)
    {
        return this.rom[bank];
    }

    public virtual short[] getVromBank(int bank)
    {
        return this.vrom[bank];
    }

    public virtual Tile[] getVromBankTiles(int bank)
    {
        return this.vromTile[bank];
    }

    public virtual bool hasBatteryRam()
    {
        return this.batteryRam;
    }

    public virtual bool hasTrainer()
    {
        return this.trainer;
    }

    public virtual void load(Stream file)
    {
        this.fileName = "";
        Stream stream = file;
        byte[] buffer = new byte[stream.Length];
        stream.Read(buffer, 0, (int) stream.Length);
        if ((buffer == null) || (buffer.Length == 0))
        {
            this.valid = false;
        }
        this.header = new short[0x10];
        for (int i = 0; i < 0x10; i++)
        {
            this.header[i] = buffer[i];
        }
        string str = new string(SupportClass.ToCharArray(SupportClass.ToByteArray(new sbyte[] { (sbyte) buffer[0], (sbyte) buffer[1], (sbyte) buffer[2], (sbyte) buffer[3] })));
        if (!str.Equals("NES" + new string(SupportClass.ToCharArray(SupportClass.ToByteArray(new sbyte[] { 0x1a })))))
        {
            this.valid = false;
        }
        else
        {
            this.romCount = this.header[4];
            this.vromCount = this.header[5] * 2;
            this.mirroring = ((this.header[6] & 1) != 0) ? 1 : 0;
            this.batteryRam = (this.header[6] & 2) != 0;
            this.trainer = (this.header[6] & 4) != 0;
            this.fourScreen = (this.header[6] & 8) != 0;
            this.mapperType = (this.header[6] >> 4) | (this.header[7] & 240);
            if (this.batteryRam)
            {
                this.loadBatteryRam();
            }
            bool flag = false;
            for (int j = 8; j < 0x10; j++)
            {
                if (this.header[j] != 0)
                {
                    flag = true;
                    break;
                }
            }
            if (flag)
            {
                this.mapperType &= 15;
            }
            this.rom = new short[this.romCount][];
            for (int k = 0; k < this.romCount; k++)
            {
                this.rom[k] = new short[0x4000];
            }
            this.vrom = new short[this.vromCount][];
            for (int m = 0; m < this.vromCount; m++)
            {
                this.vrom[m] = new short[0x1000];
            }
            this.vromTile = new Tile[this.vromCount][];
            for (int n = 0; n < this.vromCount; n++)
            {
                this.vromTile[n] = new Tile[0x100];
            }
            int num6 = 0x10;
            for (int num7 = 0; num7 < this.romCount; num7++)
            {
                for (int num8 = 0; num8 < 0x4000; num8++)
                {
                    if ((num6 + num8) >= buffer.Length)
                    {
                        break;
                    }
                    this.rom[num7][num8] = buffer[num6 + num8];
                }
                num6 += 0x4000;
            }
            for (int num9 = 0; num9 < this.vromCount; num9++)
            {
                for (int num10 = 0; num10 < 0x1000; num10++)
                {
                    if ((num6 + num10) >= buffer.Length)
                    {
                        break;
                    }
                    this.vrom[num9][num10] = buffer[num6 + num10];
                }
                num6 += 0x1000;
            }
            for (int num11 = 0; num11 < this.vromCount; num11++)
            {
                for (int num12 = 0; num12 < 0x100; num12++)
                {
                    this.vromTile[num11][num12] = new Tile();
                }
            }
            for (int num15 = 0; num15 < this.vromCount; num15++)
            {
                for (int num16 = 0; num16 < 0x1000; num16++)
                {
                    int index = num16 >> 4;
                    int sline = num16 % 0x10;
                    if (sline < 8)
                    {
                        this.vromTile[num15][index].setScanline(sline, this.vrom[num15][num16], this.vrom[num15][num16 + 8]);
                    }
                    else
                    {
                        this.vromTile[num15][index].setScanline(sline - 8, this.vrom[num15][num16 - 8], this.vrom[num15][num16]);
                    }
                }
            }
            this.valid = true;
        }
    }

    private void loadBatteryRam()
    {
    }

    public virtual bool mapperSupported()
    {
        return (((this.mapperType < mapperSupported_Renamed_Field.Length) && (this.mapperType >= 0)) && mapperSupported_Renamed_Field[this.mapperType]);
    }

    public virtual void writeBatteryRam(int address, short value_Renamed)
    {
        if ((!this.failedSaveFile && !this.batteryRam) && this.enableSave)
        {
            this.loadBatteryRam();
        }
        if ((this.batteryRam && this.enableSave) && !this.failedSaveFile)
        {
            this.saveRam[address - 0x6000] = value_Renamed;
            this.saveRamUpToDate = false;
        }
    }

    public virtual short[] BatteryRam
    {
        get
        {
            return this.saveRam;
        }
    }

    public virtual string FileName
    {
        get
        {
            FileInfo info = new FileInfo(this.fileName);
            return info.Name;
        }
    }

    public virtual short[] Header
    {
        get
        {
            return this.header;
        }
    }

    public virtual string MapperName
    {
        get
        {
            if ((this.mapperType >= 0) && (this.mapperType < mapperName.Length))
            {
                return mapperName[this.mapperType];
            }
            return ("Unknown Mapper, " + this.mapperType);
        }
    }

    public virtual int MapperType
    {
        get
        {
            return this.mapperType;
        }
    }

    public virtual int MirroringType
    {
        get
        {
            if (this.fourScreen)
            {
                return 2;
            }
            if (this.mirroring == 0)
            {
                return 1;
            }
            return 0;
        }
    }

    public virtual int RomBankCount
    {
        get
        {
            return this.romCount;
        }
    }

    public virtual bool SaveState
    {
        set
        {
            if (value && !this.batteryRam)
            {
                this.loadBatteryRam();
            }
        }
    }

    public virtual bool Valid
    {
        get
        {
            return this.valid;
        }
    }

    public virtual int VromBankCount
    {
        get
        {
            return this.vromCount;
        }
    }
}

